package com.zhxin.nettylab.nio.chapter3;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @ClassName SelectorTest
 * @Description //选择器 使用  服务器端示例
 * @Author singleZhang
 * @Email 405780096@qq.com
 * @Date 2020/12/4 0004 上午 9:17
 **/
public class SelectorTest {
    private static Selector selector;
    public static void main(String[] args){

        try{
            /*
             * 获取选择器示例
             * Selector选择器的类方法open()的内部，是向选择器SPI（SelectorProvider）发出请求，
             * 通过默认的SelectorProvider（选择器提供者）对象，获取一个新的选择器实例。
             * Java中SPI全称为（Service Provider Interface，服务提供者接口），是JDK的一种可以扩展的服务提供和发现机制。
             * Java通过SPI的方式，提供选择器的默认实现版本
             */
            selector = Selector.open();

            /*
            * 将通道注册到选择器实例
            * */
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //打开ServerSocketChannel,获取通道
            serverSocketChannel.configureBlocking(false); //设为非阻塞

            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress(8989)); //将该通道对应的serverSocket绑定到port端口
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//将通道注册到选择器上,监听"接收连接"事件

            /*
             * 选出感兴趣的IO就绪事件（选择键集合）
             * 通过Selector选择器的select()方法，选出已经注册的、已经就绪的IO事件，保存到SelectionKey选择键集合中
             * 遍历这些IO事件,进行对应的处理
             */
            while (selector.select() > 0){
                Set<SelectionKey> selectKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectKeys.iterator();

                while(keyIterator.hasNext()){
                    SelectionKey key = keyIterator.next();
                    if(key.isAcceptable()){
                        // ServerSocketChannel服务器监听通道有新连接
                        handleAccept(key);
                    } else if(key.isReadable()){
                        // 传输通道可读
                        handleRead(key);
                    } else if(key.isWritable()){
                        //传输通道可读
                        handleWrite(key);
                    }

                    //移除处理完的选择键
                    keyIterator.remove();
                }

            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 处理客户端新连接事件
     */
    private static void handleAccept(SelectionKey key) throws IOException {
        // 获取客户端连接通道
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = server.accept();
        socketChannel.configureBlocking(false);

        // 信息通过通道发送给客户端
        String msg = "Hello Client!";
        socketChannel.write(ByteBuffer.wrap(msg.getBytes()));

        // 给通道设置读事件，客户端监听到读事件后，进行读取操作
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

    /**
     * 监听到可读，处理客户端发送过来的信息
     */
    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();

        // 从通道读取数据到缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(128);
        channel.read(buffer);

        // 输出客户端发送过来的消息
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("server received msg from client：" + msg);
    }

    private static void handleWrite(SelectionKey key){

    }
}
